/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.twitter.client;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONObject;
import org.webos.java.base.Application;
import org.webos.java.base.Frame;
import org.webos.java.base.FrameType;
import org.webos.java.base.Notify;
import org.webos.java.base.RemoteMessage;
import org.webos.java.base.workspace.Workspace;
import org.webos.java.root.Log;
import org.webos.java.base.SystemManager;
import org.webos.java.base.NetworkManager;
import org.webos.java.base.grid.GridController;
import org.webos.java.base.grid.GridControllerItem;
import org.webos.java.twitter.Twitter;
import org.webos.java.twitter.Twitter.Message;
import org.webos.java.twitter.Twitter.Status;
import org.webos.java.twitter.TwitterException;


class Tweet{
    long id = 0;
    long tweetID = 0;
    boolean read = false;
    Twitter.Message message = null;
    Twitter.Status status = null;
    Twitter.User user = null;
    Date date = null;
    long time = 0;
    boolean direct = false;
    String txt = "";//!Test
}
/**
 *
 * @author Kostya
 */

@FrameType(code="main", jsClass="twitter.TwitterFrame")
public class TwitterFrame extends Frame{
    class TweetItem extends GridControllerItem<Tweet>{
        private String loginNane = "";
        private TwitterFrame frame = null;
        private String convertedText = "";

        public TweetItem(TwitterFrame aFrame, GridController aController, Tweet aTweet, String aLoginName) {
            super(aController, aTweet, Long.toString(aTweet.id));
            frame = aFrame;
            this.loginNane = aLoginName;
            convertedText = convertIncomingText(aTweet.status!=null?
                    aTweet.status.text:
                    aTweet.message!=null?
                        ("<b>D: </b>"+aTweet.message.getText()):
                        aTweet.txt);
        }

        @Override
        public void objectTOJSON(JSONObject object) {
            Tweet aTweet = getObject();
            try {
                object.put("read", aTweet.read);
                object.put("avatar", aTweet.user.profileImageUrl.toString());
                object.put("title", aTweet.user.screenName);
                object.put("tweetID", aTweet.tweetID);
                object.put("date", TwitterFrame.dateFormat.format(
                        frame.getApplication().getSession().normalizeDate(aTweet.date)));
                object.put("removable", loginNane.equalsIgnoreCase(aTweet.user.screenName));
                object.put("text", convertedText);
            } catch (Throwable t) {
                Log.error(this, "Exception thrown: " + t.getMessage());
                t.printStackTrace();
            }

        }

    }

    private boolean showNotify = true;
    private int globalID = 0;
    static DateFormat dateFormat = new SimpleDateFormat("d.MM'@'h:mma");
    private static int numberOfMessagesToKeep = 40;
    private static int maxNumberOfMessagesToKeep = 1000;
    private static int maxPages = 20;
    private static final int MAX_WORD_LENGTH = 20;
    private static final String WORD_REGEXP = "(\\S+)";
    private static final String URL_REGEXP2 = "((https?|ftp)\\://((\\[?(\\d{1,3}\\.){3}\\d{1,3}\\]?)|(([-a-zA-Z0-9]+\\.)+[a-zA-Z]{2,6}))(\\:\\d+)?(/[-a-zA-Z0-9._\\?,'+\\&\\%\\$\\#=~\\\\\\;/]+)*)";
    private static final String URL_REGEXP = "((https?:\\/\\/|www\\.)([-\\w\\.]+)+(:\\d+)?(\\/([\\w\\/_\\.-]*(\\?\\S+)?)?)?)";
    private static final String TWITTER_REGEXP = "\\@\\w+";
    private static final String HASH_REGEXP = "(\\s\\#\\w+|^\\#\\w+)";
    static final String[] urlTrims = {"http://tinyurl.com/", "http://bit.ly/",
        "http://snurl.com/", "http://is.gd/", "http://kip.ru/", "http://tr.im/",
        "http://clck.ru/", "http://shorturl.com/", "http://twurl.nl/",
        "http://snipurl.com/", "http://sn.im/", "http://budurl.com/",
        "http://burnurl.com/", "http://b23.ru/", "http://cli.gs/", "http://uurl.in/",
        "http://ping.fm/", "http://adjix.com/", "http://shorl.com/", "http://short.io/",
        "http://a.gd/"
    };

    private String settingName = null;
    private String loginName = "";
    private String password = "";
    private int updateMinutes = 5;
    private Twitter twitter = null;

    private long lastStatus = 0;
    private long lastReply = 0;
    private long lastDMessage = 0;
    private long replyTo = 0;
    private String replyToUser = "";
    private Timer timer = null;
    private int unreadCount = 0;
    private boolean paused = false;
    private Set<String> newTweetsUsers = new TreeSet<String>();
    final private GridController<TweetItem> controller = new GridController(this, true, 15);


    public TwitterFrame(TwitterApp app, Workspace aWorkspace, String aSettingName) {
        super(app, aWorkspace);
        setShowStatus(true);
        settingName = aSettingName;
        setIcon("tweet_icns tweet_icn_main");
        controller.setComparator(new Comparator<TweetItem>() {

            public int compare(TweetItem o1, TweetItem o2) {
                return o1.getObject().time==o2.getObject().time?
                    0:
                    o1.getObject().time>o2.getObject().time?-1:1;
            }
        });
    }

    private void updateByTimer(){
        if(paused)
            return;
        if(timer==null){
            if(!updateTimeline()){
//                paused = true;
//                updateNewTweetsCount();
//                return;
                //Show twitter update error
//                Notify notify = new Notify("Twitter update error", "Error updating twitter timeline. Network ");
//                notify.setIcon("tweet_icns tweet_icn_main");
//                notify.setTimeout(5000);
//                getApplication().addNotify(notify, this);
            }
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    timer = null;
                    updateByTimer();
                }
            }, 1000*60*updateMinutes);
        }
    }

    public void init(){
        try {
            sendMessage("tweetsLoading", new JSONObject());
            JSONObject data = new JSONObject(getApplication().getOption(settingName, "{}"));
            loginName = data.getString("username");
            updateMinutes = data.optInt("minutes", 15);
            showNotify = data.optBoolean("show_notify", true);
//            Log.debug(this, "Ready to work with twitter", loginName, password, updateMinutes);
            setTitle("Twitter: "+loginName);
            twitter = new Twitter();
            twitter.setToken(data.optString("token"), data.optString("tokenSecret"));
            controller.clear();
            lastDMessage = 0;
            lastReply = 0;
            lastStatus = 0;
            replyTo = 0;
            replyToUser = "";
            unreadCount = 0;
            paused = false;//!Test
            if(timer!=null)
                timer.cancel();
            timer = null;
            updateByTimer();//!Test
//            testGrid();
            controller.setSilent(false);
            sendMessage("loadGrid", controller.loadGrid(new JSONObject()));
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

//    private Tweet createTestTweet(long id, JSONObject user) throws TwitterException{
//        Tweet tw0 = new Tweet();
//        tw0.id = id;
//        tw0.time = tw0.id;
//        tw0.date = new Date();
//        tw0.user = new Twitter.User(user, null);
//        tw0.txt = "Message "+id;
//        return tw0;
//    }
//
//    private void testGrid() throws TwitterException{
//        final TwitterFrame instance = this;
//        JSONObject user = new JSONObject();
//        user.putOpt("id", 0);
//        user.putOpt("name", "user");
//        user.putOpt("screen_name", "screen_name");
//        user.putOpt("profile_image_url", "profile_image_url");
//        user.putOpt("protected", false);
//        final TweetItem tw0 = new TweetItem(this, controller, createTestTweet(0, user), loginName);
//        final TweetItem tw1 = new TweetItem(this, controller, createTestTweet(1, user), loginName);
//        final TweetItem tw2 = new TweetItem(this, controller, createTestTweet(2, user), loginName);
//        final TweetItem tw3 = new TweetItem(this, controller, createTestTweet(3, user), loginName);
//        final TweetItem tw4 = new TweetItem(this, controller, createTestTweet(4, user), loginName);
//        final TweetItem tw5 = new TweetItem(this, controller, createTestTweet(5, user), loginName);
//        final TweetItem tw6 = new TweetItem(this, controller, createTestTweet(6, user), loginName);
//        final TweetItem tw7 = new TweetItem(this, controller, createTestTweet(7, user), loginName);
//        final TweetItem tw8 = new TweetItem(this, controller, createTestTweet(8, user), loginName);
//        final TweetItem tw9 = new TweetItem(this, controller, createTestTweet(9, user), loginName);
//        final TweetItem tw10 = new TweetItem(this, controller, createTestTweet(10, user), loginName);
//        final TweetItem tw11 = new TweetItem(this, controller, createTestTweet(11, user), loginName);
//        Log.debug(this, "Test phase#1");
//        controller.appendItem(tw0);
//        controller.appendItem(tw1);
//        controller.appendItem(tw2);
//        controller.appendItem(tw3);
//        controller.appendItem(tw4);
//        controller.appendItem(tw5);
//        controller.appendItem(tw6);
//        controller.appendItem(tw7);
//        Timer t = new Timer();
//        t.schedule(new TimerTask() {
//
//            @Override
//            public void run() {
//                Log.debug(this, "Test phase#2");
//                controller.appendItem(tw8);
////                controller.appendItem(tw9);
//                //10 items
//            }
//        }, 5000);
//        t.schedule(new TimerTask() {
//
//            @Override
//            public void run() {
//                Log.debug(this, "Test phase#3");
//                controller.removeItem(tw8);
//                //9 items
//            }
//        }, 10000);
//        t.schedule(new TimerTask() {
//
//            @Override
//            public void run() {
//                Log.debug(this, "Test phase#4");
//                controller.removeItem(tw9);
//                controller.removeItem(tw0);
//                controller.removeItem(tw5);
//                controller.removeItem(tw7);
//                controller.removeItem(tw3);
//                controller.checkPageCount();
//                //4 items
//            }
//        }, 15000);
//        t.schedule(new TimerTask() {
//
//            @Override
//            public void run() {
//                Log.debug(this, "Test phase#5");
//                while(controller.getItemCount()>0)
//                    controller.removeItem(controller.getItem(0));
//                controller.checkPageCount();
//                //0 items
//            }
//        }, 25000);
//        t.schedule(new TimerTask() {
//
//            @Override
//            public void run() {
//                Log.debug(this, "Test phase#6");
//                controller.appendItem(tw0);
//                controller.appendItem(tw1);
//                controller.appendItem(tw2);
//                controller.appendItem(tw3);
//                controller.appendItem(tw4);
//                controller.appendItem(tw5);
//                controller.appendItem(tw6);
//                controller.appendItem(tw7);
//                controller.appendItem(tw8);
//                controller.appendItem(tw9);
//                controller.appendItem(tw10);
//                controller.appendItem(tw11);
//                //12 items
//            }
//        }, 30000);
//    }
//
    @RemoteMessage
    public void playPause(JSONObject aObject){
        paused = !paused;
        if(paused)
            updateNewTweetsCount();
        else
            updateByTimer();
    }

    @RemoteMessage
    public JSONObject destroyStatus(JSONObject aData){
        try {
            synchronized(controller){
                TweetItem item = controller.getItem(aData.getString("id"));
                if(item!=null){
                    twitter.destroyStatus(item.getObject().tweetID);
                    if(!item.getObject().read)
                        unreadCount--;
                    controller.removeItem(item);
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
            return NetworkManager.getErrorObject(703, "Can't remove status. Invalid username/password or network error");
        }
        return null;
    }


    @RemoteMessage
    public JSONObject updateStatus(JSONObject aData){
        try {
            String messageToSend = convertOutgoingText(aData.getString("message"));
            List<String> messages = twitter.splitMessage(messageToSend);
            long sendReplyTo = 0;
            if(replyTo>0 && messageToSend.startsWith("@"+replyToUser+" ")){
                sendReplyTo = replyTo;
            }
            for (String m : messages) {
                twitter.updateStatus(m, sendReplyTo);
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
            return NetworkManager.getErrorObject(702, "Can't update status. Invalid username/password or network error");
        }
        return null;
    }

    @RemoteMessage
    public void startReply(JSONObject aData){
        try {
            replyTo = aData.getLong("replyto");
            replyToUser = aData.getString("replytouser");
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage
    public JSONObject updateTwitter(JSONObject aData){
        try {
            updateTimeline();
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    };

    @RemoteMessage
    public JSONObject showSettings(JSONObject aData){
        try {
            getApplication().getSession().
                    addDialog(new SettingsDialog((TwitterApp) getApplication(), settingName));
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    private String convertOutgoingText(String aInput){
        try {
            Pattern urlPattern = Pattern.compile(URL_REGEXP2, Pattern.CASE_INSENSITIVE);
            Matcher m = urlPattern.matcher(aInput);
            StringBuffer sb = new StringBuffer();
            boolean found = false;
            while(m.find()){
                found = true;
//                Log.debug(this, "Found link at "+m.start()+" to "+m.end());
                String link = m.group();
                if(link.length()>20){
                    try {
                        Map<String, String> vars = twitter.asMap("longurl", link);
                        String newLink = twitter.get2("http://is.gd/api.php", vars, null);
                        link = newLink;
                    } catch (Throwable t) {
                        Log.error(this, "Exception thrown: " + t.getMessage());
                        t.printStackTrace();
                    }
                }
                m.appendReplacement(sb, link);
            }
            m.appendTail(sb);
            return sb.toString();
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return aInput;
    }

    String getHiddenLink(String aLink){
        boolean found = false;
        for (String utrltrim : urlTrims) {
            if(aLink.startsWith(utrltrim)){
                found = true;
                break;
            }
        }
        if(found){
            try {
                SystemManager.setupProxy(getApplication().getSession());
                HttpURLConnection conn = (HttpURLConnection) new URL(aLink).openConnection();
                conn.setInstanceFollowRedirects(false);
                InputStream is = conn.getInputStream();
//                Log.debug("TwitterFrame", "Response["+aLink+"]: "+conn.getResponseCode());
                if(conn.getResponseCode()>300 && conn.getResponseCode()<308){
                    String field = null;
                    int i = 0;
//                    while((field = conn.getHeaderField(i))!=null){
//                        Log.debug("TwitterFrame", "Header["+aLink+"]:", field, conn.getHeaderFieldKey(i));
//                        i++;
//                    }
                    String location = conn.getHeaderField("Location");
                    conn.disconnect();
                    if(location!=null)
                        return location;
                }
            } catch (Throwable t) {
                Log.error("TwitterFrame", "Exception thrown: " + t.getMessage());
                t.printStackTrace();
            }
        }
        return aLink;
    }

    String convertIncomingText(String aInput){
        try {
            Pattern wordPattern = Pattern.compile(WORD_REGEXP);
            Matcher m = wordPattern.matcher(aInput);

            StringBuffer sb = new StringBuffer();
            while(m.find()){
                String word = m.group();
                StringBuffer buffer = new StringBuffer();
                if(!word.startsWith("http://") && !word.startsWith("https://") && !word.startsWith("(http://")){
                    while(word.length()>MAX_WORD_LENGTH){
                        buffer.append(word.substring(0, MAX_WORD_LENGTH)+((char)8203));
                        word = word.substring(MAX_WORD_LENGTH);
                    }
                }
                buffer.append(word);
                m.appendReplacement(sb, buffer.toString());
            }
            m.appendTail(sb);
            Pattern urlPattern = Pattern.compile(URL_REGEXP2, Pattern.CASE_INSENSITIVE);
            m = urlPattern.matcher(sb.toString());

            sb = new StringBuffer();
            boolean found = false;
            while(m.find()){
                found = true;
//                Log.debug(this, "Found link at "+m.start()+" to "+m.end());
                String link = m.group();
                String link2 = new String(link);
                if(!link2.startsWith("http://") && !link2.startsWith("https://")){
                    link2 = "http://"+link2;
                }
                if(link.length()>25){
                    link = link.substring(0, 22)+"...";
                }
                m.appendReplacement(sb, "<a href=\""+link2+"\" title=\""+getHiddenLink(link2)+"\" class=\"tweet_link\" target=\"_blank\">"+link+"</a>");
            }
            m.appendTail(sb);
//            if(!found)
//                sb.append(aInput);
            Pattern twPattern = Pattern.compile(TWITTER_REGEXP, Pattern.CASE_INSENSITIVE);
            String current = sb.toString();
            m = twPattern.matcher(current);
            sb = new StringBuffer();
            while(m.find()){
//                Log.debug(this, "Found link at "+m.start()+" to "+m.end()+", "+m.group());
                String user = m.group().trim().substring(1);
                m.appendReplacement(sb, "<a href=\"http://twitter.com/"+user+"\" class=\"tweet_link_user\" target=\"_blank\">@"+user+"</a>");
            }
            m.appendTail(sb);
            Pattern tags = Pattern.compile(HASH_REGEXP, Pattern.CASE_INSENSITIVE);
            m = tags.matcher(sb.toString());
            sb = new StringBuffer();
            while(m.find()){
                String tag = m.group();
                m.appendReplacement(sb,
                        "<a href=\"http://search.twitter.com/search?q="+
                        URLEncoder.encode(tag.trim(), "utf-8")+
                        "\" class=\"tweet_link_tag\" target=\"_blank\">"+tag+"</a>");
            }
            m.appendTail(sb);
//            if(!found)
//                sb.append(current);
            return sb.toString();
        } catch (Throwable t) {
            Log.error("Twitter", "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return aInput;
    }

    @RemoteMessage
    public JSONObject markAllRead(JSONObject aData){
        try {
            synchronized(controller){
                controller.setSilent(true);
                for (int i = 0; i < controller.getItemCount(); i++) {
                    TweetItem item = controller.getItem(i);
                    item.getObject().read = true;
                }
                unreadCount = 0;
            }
            controller.setSilent(false);
            updateNewTweetsCount();
            return controller.loadGrid(aData);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    
    @RemoteMessage
    public void markAsUnread(JSONObject aData){
        try {
            String id = aData.optString("id", "");
            synchronized(controller){
                for (int i = 0; i < controller.getItemCount(); i++) {
                    TweetItem item = controller.getItem(i);
                    if(id.equals(item.getId())){
                        if(item.getObject().read){
                            item.getObject().read = false;
                            unreadCount++;
                        }
                    }
                }
                updateNewTweetsCount();
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    public void tweetSelected(JSONObject aData){
        synchronized(controller){
            controller.selectionChanged(aData);
            if(controller.getSelected()!=null){
                if(!controller.getSelected().getObject().read){
                    controller.getSelected().getObject().read = true;
                    unreadCount--;
                }
            }
            updateNewTweetsCount();
        }
    }

    private int addNewMessages(List aEntries){
        int tweetsAdded = 0;
        for (Object object : aEntries) {
            Tweet newTweet = new Tweet();
            if(object instanceof Twitter.Status){
                Status status = (Status) object;
                newTweet.id = status.id+(globalID++);
                newTweet.tweetID = status.id;
                newTweet.status = status;
                newTweet.user = status.getUser();
                newTweet.date = status.getCreatedAt();
            }else{
                Message message = (Message) object;
                newTweet.id = message.getId()+(globalID++);
                newTweet.tweetID = message.getId();
                newTweet.message = message;
                newTweet.user = message.getSender();
                newTweet.date = message.getCreatedAt();
                newTweet.direct = true;
            }
            newTweet.time = newTweet.date.getTime();
            newTweetsUsers.add(newTweet.user.getScreenName());
//            Log.debug(this, "Profile image: "+newTweet.user.getProfileImageUrl());
            boolean added = false;
            for (int i = controller.getItemCount()-1; i>=0 && controller.getItemCount()>numberOfMessagesToKeep ; i--) {
                boolean maxExceed = controller.getItemCount()>maxNumberOfMessagesToKeep;
                Tweet tweet = controller.getItem(i).getObject();
                if(tweet.read || maxExceed){
//                    if(!tweet.read)
//                        tweetsAdded--;
                    controller.removeItem(controller.getItem(i));
                    i--;
                }
            }
            for (int i = 0; i < controller.getItemCount(); i++) {
                Tweet tweet = controller.getItem(i).getObject();
                if(newTweet.tweetID==tweet.tweetID){
                    added = true;
                    break;
                }
            }
            if(!added){
                controller.appendItem(new TweetItem(this, controller, newTweet, loginName));
                tweetsAdded++;
            }
        }
        return tweetsAdded;
    }

    private void updateNewTweetsCount(){
        try {
            String title = "Twitter";
            if(unreadCount>0)
                title = "["+unreadCount+"] Twitter";
            setTitle(title+": "+loginName);
            getApplication().setName(title);
            JSONObject newTweetsObject = new JSONObject();
            newTweetsObject.put("count", unreadCount);
            newTweetsObject.put("paused", paused);
            sendMessage("newTweets", newTweetsObject);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    private boolean updateTimeline(){
        newTweetsUsers.clear();
        try {
            SystemManager.setupProxy(getApplication().getSession());
            int lNewTweets = 0;
            int tmp = 0;

            int page = 0;
            long newID = lastStatus;
            int items = 0;
            do{
                List<Twitter.Status> statuses = twitter.getFriendsTimeline(lastStatus, page);
                items = statuses.size();
                if(items>0){
                    if(page==0)
                        newID = statuses.get(0).id;
                    tmp = addNewMessages(statuses);
                    lNewTweets += tmp;
                    unreadCount += tmp;
                }
                page++;
            }while(items==20 && page<maxPages && lastStatus>0);
            lastStatus = newID;

            page = 0;
            newID = lastReply;
            items = 0;
            do{
                List<Twitter.Status> statuses = twitter.getReplies(lastReply, page);
                items = statuses.size();
                if(items>0){
                    if(page==0)
                        newID = statuses.get(0).id;
                    tmp = addNewMessages(statuses);
                    lNewTweets += tmp;
                    unreadCount += tmp;
                }
                page++;
            }while(items==20 && page<maxPages && lastReply>0);
            lastReply = newID;

            page = 0;
            newID = lastDMessage;
            items = 0;
            do{
                List<Twitter.Message> statuses = twitter.getDirectMessages(lastDMessage, page);
                items = statuses.size();
                if(items>0){
                    if(page==0)
                        newID = statuses.get(0).getId();
                    tmp = addNewMessages(statuses);
                    lNewTweets += tmp;
                    unreadCount += tmp;
                }
                page++;
            }while(items==20 && page<maxPages && lastDMessage>0);
            lastDMessage = newID;

            Log.debug(this, "TimeLine now is: "+controller.getItemCount());

            setStatus("Updated "+dateFormat.format(
                    getApplication().getSession().normalizeDate(new Date())));
            if(lNewTweets>0){
                StringBuffer text = new StringBuffer("Received "+lNewTweets+" new tweets from ");
                int i = 0;
                for (String name : newTweetsUsers) {
                    if(i>0)
                        text.append(", ");
                    if(i>4){
                        text.append(" and "+(newTweetsUsers.size()-i)+" other users");
                        break;
                    }else{
                        text.append(name);
                    }
                    i++;
                }
                Notify notify = new Notify(lNewTweets==1?("1 new tweet"):(lNewTweets+" new tweets"), text.toString());
                notify.setIcon("tweet_icns tweet_icn_main");
                notify.setTimeout(5000);
                if(showNotify)
                    getApplication().addNotify(notify, this);
                controller.checkPageCount();
            }
            return true;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
//            getApplication().sendError(701, "Can't update timeline. Invalid username/password or network error");
            Notify error = new Notify("Error updating timeline", "There was an error updating twitter timeline");
            error.setIcon("tweet_icns tweet_icn_main");
            error.setForceAutoRemove(true);
            getApplication().addNotify(error, this);
        }finally{
            updateNewTweetsCount();
        }
        return false;
    }

    @RemoteMessage
    public void selectionChanged(JSONObject aObject){
        tweetSelected(aObject);
        controller.selectionChanged(aObject);
    }

    @RemoteMessage
    public JSONObject loadGrid(JSONObject aObject){
        if(aObject.optBoolean("sendCounter", false))
            updateNewTweetsCount();
        return controller.loadGrid(aObject);
    }

    @Override
    public void terminate() {
        paused = true;
    }


}
